Stripe Integration
Master Stripe payment processing integration for robust, PCI-compliant payment flows including checkout, subscriptions, webhooks, and refunds.
When to Use This Skill
- Implementing payment processing in web/mobile applications
- Setting up subscription billing systems
- Handling one-time payments and recurring charges
- Processing refunds and disputes
- Managing customer payment methods
- Implementing SCA (Strong Customer Authentication) for European payments
- Building marketplace payment flows with Stripe Connect
Core Concepts
1. Payment Flows
Checkout Session (Hosted)
- Stripe-hosted payment page
- Minimal PCI compliance burden
- Fastest implementation
- Supports one-time and recurring payments
Payment Intents (Custom UI)
- Full control over payment UI
- Requires Stripe.js for PCI compliance
- More complex implementation
- Better customization options
Setup Intents (Save Payment Methods)
- Collect payment method without charging
- Used for subscriptions and future payments
- Requires customer confirmation
2. Webhooks
Critical Events:
payment_intent.succeeded: Payment completedpayment_intent.payment_failed: Payment failedcustomer.subscription.updated: Subscription changedcustomer.subscription.deleted: Subscription canceledcharge.refunded: Refund processedinvoice.payment_succeeded: Subscription payment successful
3. Subscriptions
Components:
- Product: What you're selling
- Price: How much and how often
- Subscription: Customer's recurring payment
- Invoice: Generated for each billing cycle
4. Customer Management
- Create and manage customer records
- Store multiple payment methods
- Track customer metadata
- Manage billing details
Quick Start
python1import stripe 2 3stripe.api_key = "sk_test_..." 4 5# Create a checkout session 6session = stripe.checkout.Session.create( 7 payment_method_types=['card'], 8 line_items=[{ 9 'price_data': { 10 'currency': 'usd', 11 'product_data': { 12 'name': 'Premium Subscription', 13 }, 14 'unit_amount': 2000, # $20.00 15 'recurring': { 16 'interval': 'month', 17 }, 18 }, 19 'quantity': 1, 20 }], 21 mode='subscription', 22 success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}', 23 cancel_url='https://yourdomain.com/cancel', 24) 25 26# Redirect user to session.url 27print(session.url)
Payment Implementation Patterns
Pattern 1: One-Time Payment (Hosted Checkout)
python1def create_checkout_session(amount, currency='usd'): 2 """Create a one-time payment checkout session.""" 3 try: 4 session = stripe.checkout.Session.create( 5 payment_method_types=['card'], 6 line_items=[{ 7 'price_data': { 8 'currency': currency, 9 'product_data': { 10 'name': 'Purchase', 11 'images': ['https://example.com/product.jpg'], 12 }, 13 'unit_amount': amount, # Amount in cents 14 }, 15 'quantity': 1, 16 }], 17 mode='payment', 18 success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}', 19 cancel_url='https://yourdomain.com/cancel', 20 metadata={ 21 'order_id': 'order_123', 22 'user_id': 'user_456' 23 } 24 ) 25 return session 26 except stripe.error.StripeError as e: 27 # Handle error 28 print(f"Stripe error: {e.user_message}") 29 raise
Pattern 2: Custom Payment Intent Flow
python1def create_payment_intent(amount, currency='usd', customer_id=None): 2 """Create a payment intent for custom checkout UI.""" 3 intent = stripe.PaymentIntent.create( 4 amount=amount, 5 currency=currency, 6 customer=customer_id, 7 automatic_payment_methods={ 8 'enabled': True, 9 }, 10 metadata={ 11 'integration_check': 'accept_a_payment' 12 } 13 ) 14 return intent.client_secret # Send to frontend 15 16# Frontend (JavaScript) 17""" 18const stripe = Stripe('pk_test_...'); 19const elements = stripe.elements(); 20const cardElement = elements.create('card'); 21cardElement.mount('#card-element'); 22 23const {error, paymentIntent} = await stripe.confirmCardPayment( 24 clientSecret, 25 { 26 payment_method: { 27 card: cardElement, 28 billing_details: { 29 name: 'Customer Name' 30 } 31 } 32 } 33); 34 35if (error) { 36 // Handle error 37} else if (paymentIntent.status === 'succeeded') { 38 // Payment successful 39} 40"""
Pattern 3: Subscription Creation
python1def create_subscription(customer_id, price_id): 2 """Create a subscription for a customer.""" 3 try: 4 subscription = stripe.Subscription.create( 5 customer=customer_id, 6 items=[{'price': price_id}], 7 payment_behavior='default_incomplete', 8 payment_settings={'save_default_payment_method': 'on_subscription'}, 9 expand=['latest_invoice.payment_intent'], 10 ) 11 12 return { 13 'subscription_id': subscription.id, 14 'client_secret': subscription.latest_invoice.payment_intent.client_secret 15 } 16 except stripe.error.StripeError as e: 17 print(f"Subscription creation failed: {e}") 18 raise
Pattern 4: Customer Portal
python1def create_customer_portal_session(customer_id): 2 """Create a portal session for customers to manage subscriptions.""" 3 session = stripe.billing_portal.Session.create( 4 customer=customer_id, 5 return_url='https://yourdomain.com/account', 6 ) 7 return session.url # Redirect customer here
Webhook Handling
Secure Webhook Endpoint
python1from flask import Flask, request 2import stripe 3 4app = Flask(__name__) 5 6endpoint_secret = 'whsec_...' 7 8@app.route('/webhook', methods=['POST']) 9def webhook(): 10 payload = request.data 11 sig_header = request.headers.get('Stripe-Signature') 12 13 try: 14 event = stripe.Webhook.construct_event( 15 payload, sig_header, endpoint_secret 16 ) 17 except ValueError: 18 # Invalid payload 19 return 'Invalid payload', 400 20 except stripe.error.SignatureVerificationError: 21 # Invalid signature 22 return 'Invalid signature', 400 23 24 # Handle the event 25 if event['type'] == 'payment_intent.succeeded': 26 payment_intent = event['data']['object'] 27 handle_successful_payment(payment_intent) 28 elif event['type'] == 'payment_intent.payment_failed': 29 payment_intent = event['data']['object'] 30 handle_failed_payment(payment_intent) 31 elif event['type'] == 'customer.subscription.deleted': 32 subscription = event['data']['object'] 33 handle_subscription_canceled(subscription) 34 35 return 'Success', 200 36 37def handle_successful_payment(payment_intent): 38 """Process successful payment.""" 39 customer_id = payment_intent.get('customer') 40 amount = payment_intent['amount'] 41 metadata = payment_intent.get('metadata', {}) 42 43 # Update your database 44 # Send confirmation email 45 # Fulfill order 46 print(f"Payment succeeded: {payment_intent['id']}") 47 48def handle_failed_payment(payment_intent): 49 """Handle failed payment.""" 50 error = payment_intent.get('last_payment_error', {}) 51 print(f"Payment failed: {error.get('message')}") 52 # Notify customer 53 # Update order status 54 55def handle_subscription_canceled(subscription): 56 """Handle subscription cancellation.""" 57 customer_id = subscription['customer'] 58 # Update user access 59 # Send cancellation email 60 print(f"Subscription canceled: {subscription['id']}")
Webhook Best Practices
python1import hashlib 2import hmac 3 4def verify_webhook_signature(payload, signature, secret): 5 """Manually verify webhook signature.""" 6 expected_sig = hmac.new( 7 secret.encode('utf-8'), 8 payload, 9 hashlib.sha256 10 ).hexdigest() 11 12 return hmac.compare_digest(signature, expected_sig) 13 14def handle_webhook_idempotently(event_id, handler): 15 """Ensure webhook is processed exactly once.""" 16 # Check if event already processed 17 if is_event_processed(event_id): 18 return 19 20 # Process event 21 try: 22 handler() 23 mark_event_processed(event_id) 24 except Exception as e: 25 log_error(e) 26 # Stripe will retry failed webhooks 27 raise
Customer Management
python1def create_customer(email, name, payment_method_id=None): 2 """Create a Stripe customer.""" 3 customer = stripe.Customer.create( 4 email=email, 5 name=name, 6 payment_method=payment_method_id, 7 invoice_settings={ 8 'default_payment_method': payment_method_id 9 } if payment_method_id else None, 10 metadata={ 11 'user_id': '12345' 12 } 13 ) 14 return customer 15 16def attach_payment_method(customer_id, payment_method_id): 17 """Attach a payment method to a customer.""" 18 stripe.PaymentMethod.attach( 19 payment_method_id, 20 customer=customer_id 21 ) 22 23 # Set as default 24 stripe.Customer.modify( 25 customer_id, 26 invoice_settings={ 27 'default_payment_method': payment_method_id 28 } 29 ) 30 31def list_customer_payment_methods(customer_id): 32 """List all payment methods for a customer.""" 33 payment_methods = stripe.PaymentMethod.list( 34 customer=customer_id, 35 type='card' 36 ) 37 return payment_methods.data
Refund Handling
python1def create_refund(payment_intent_id, amount=None, reason=None): 2 """Create a refund.""" 3 refund_params = { 4 'payment_intent': payment_intent_id 5 } 6 7 if amount: 8 refund_params['amount'] = amount # Partial refund 9 10 if reason: 11 refund_params['reason'] = reason # 'duplicate', 'fraudulent', 'requested_by_customer' 12 13 refund = stripe.Refund.create(**refund_params) 14 return refund 15 16def handle_dispute(charge_id, evidence): 17 """Update dispute with evidence.""" 18 stripe.Dispute.modify( 19 charge_id, 20 evidence={ 21 'customer_name': evidence.get('customer_name'), 22 'customer_email_address': evidence.get('customer_email'), 23 'shipping_documentation': evidence.get('shipping_proof'), 24 'customer_communication': evidence.get('communication'), 25 } 26 )
Testing
python1# Use test mode keys 2stripe.api_key = "sk_test_..." 3 4# Test card numbers 5TEST_CARDS = { 6 'success': '4242424242424242', 7 'declined': '4000000000000002', 8 '3d_secure': '4000002500003155', 9 'insufficient_funds': '4000000000009995' 10} 11 12def test_payment_flow(): 13 """Test complete payment flow.""" 14 # Create test customer 15 customer = stripe.Customer.create( 16 email="test@example.com" 17 ) 18 19 # Create payment intent 20 intent = stripe.PaymentIntent.create( 21 amount=1000, 22 currency='usd', 23 customer=customer.id, 24 payment_method_types=['card'] 25 ) 26 27 # Confirm with test card 28 confirmed = stripe.PaymentIntent.confirm( 29 intent.id, 30 payment_method='pm_card_visa' # Test payment method 31 ) 32 33 assert confirmed.status == 'succeeded'
Resources
- references/checkout-flows.md: Detailed checkout implementation
- references/webhook-handling.md: Webhook security and processing
- references/subscription-management.md: Subscription lifecycle
- references/customer-management.md: Customer and payment method handling
- references/invoice-generation.md: Invoicing and billing
- assets/stripe-client.py: Production-ready Stripe client wrapper
- assets/webhook-handler.py: Complete webhook processor
- assets/checkout-config.json: Checkout configuration templates
Best Practices
- Always Use Webhooks: Don't rely solely on client-side confirmation
- Idempotency: Handle webhook events idempotently
- Error Handling: Gracefully handle all Stripe errors
- Test Mode: Thoroughly test with test keys before production
- Metadata: Use metadata to link Stripe objects to your database
- Monitoring: Track payment success rates and errors
- PCI Compliance: Never handle raw card data on your server
- SCA Ready: Implement 3D Secure for European payments
Common Pitfalls
- Not Verifying Webhooks: Always verify webhook signatures
- Missing Webhook Events: Handle all relevant webhook events
- Hardcoded Amounts: Use cents/smallest currency unit
- No Retry Logic: Implement retries for API calls
- Ignoring Test Mode: Test all edge cases with test cards